//
//  analyzefilesystem.c
//  LinuxPrinciplesandApplications
//
//  Created by SeanFelix‘sMacBook on 2023/12/3.
//

#include "analyzefilesystem.h"

void parse_superblock(FILE *fs, ext3_super_block *sb){
    
    // Ext3文件系统中：磁盘的开始1024字节是保留给引导块（boot block）的。这个区域通常留空。超级块紧随引导块之后，从第1024 字节开始。
    fseek(fs, 1024, SEEK_SET);  // 使用fseek函数将文件流fs的位置设置为从文件开始处向前1024字节的地方。SEEK_SET 表示文件位置的偏移是从文件开始处计算的。
    fread(sb, sizeof(ext3_super_block), 1, fs); // 这行代码从文件流fs读取数据到sb变量中，sb变量是 struct ext3_super_block 类型的一个实例。sizeof(struct ext3_super_block) 确定要读取的字节数，它是基于struct ext3_super_block 的大小。这个fread调用读取一个超级块的大小的数据。

    printf("Superblock Information:\n");
    printf("    Total Inodes: %u\n", sb->s_inodes_count);
    printf("    Total Blocks: %u\n", sb->s_blocks_count);
    printf("    Reserved Blocks: %u\n", sb->s_r_blocks_count);
    printf("    Free Blocks: %u\n", sb->s_free_blocks_count);
    printf("    Free Inodes: %u\n", sb->s_free_inodes_count);
    printf("    First Block: %u\n", sb->s_first_block);
    printf("    Block Size: %u\n", 1024 << sb->s_log_block_size);
    printf("    Fragment Size: %u\n", 1024 << sb->s_log_frag_size);
    printf("    Blocks Per Group: %u\n", sb->s_blocks_per_group);
    printf("    Fragments Per Group: %u\n", sb->s_frags_per_group);
    printf("    Inodes Per Group: %u\n", sb->s_inodes_per_group);
}

size_t read_gdt(FILE *fs, ext3_super_block *sb, ext3_group_desc **gdt_addr){
    // 根据超级块信息读取组描述符表（GDT, Group Descriptor Table）
    // 计算文件系统中的块组数量。它将文件系统中的总块数除以每个块组中的块数。由于块数可能不是块组大小的整数倍，所以在除法之前先加上sb->s_blocks_per_group-1来实现向上取整
    int block_group_count = (sb->s_blocks_count + sb->s_blocks_per_group - 1) / sb->s_blocks_per_group;
    // 这行代码使用malloc函数为每个块组的组描述符分配足够的内存。sizeof(struct ext3_group_desc) 计算单个组描述符的大小，乘以块组数得到总的所需内存大小
    *gdt_addr = malloc(block_group_count * sizeof(ext3_group_desc));
    // 这行代码使用fseek 定位到组描述符表的开始位置。组描述符表紧跟在超级块之后。因为超级块位于第一个块之后（s_first_block 通常为1），所以要跳过超级块，即定位到第二个块的开始。BLOCK_SIZE 是文件系统的块大小（通常为 1024 字节）
    fseek(fs, BLOCK_SIZE * (sb->s_first_block + 1), SEEK_SET);
    // 这行代码从文件中读取组描述符表到之前分配的内存中。它读取block_group_count个struct ext3_group_desc 结构。每个组描述符包含了关于一个块组的信息，例如块位图、inode位图、inode表的位置，以及空闲块和inode的数量。
    fread(*gdt_addr, sizeof(ext3_group_desc), block_group_count, fs);
    
    return block_group_count;
}

void parse_block_bitmap(FILE *fs, ext3_super_block *sb, ext3_group_desc *gdt, int i){
    // 解析块位图
    // 数组，用于存储从文件系统中读取的块位图，表示文件系统中的块是否被占用
    unsigned char block_bitmap[BLOCK_SIZE];
    // 定位到块位图的起始位置，块位图所在块的块号乘以BLOCK_SIZE得到位图的实际字节偏移量
    fseek(fs, BLOCK_SIZE * gdt[i].bg_block_bitmap, SEEK_SET);
    // 从文件系统读取块位图到block_bitmap数组中
    fread(block_bitmap, sizeof(block_bitmap), 1, fs);

    // 找出第一个空闲块号
    // 这段代码遍历块位图的每个字节（8位），每位代表一个块的使用状态（0表示空闲，1表示占用），因此 sb->s_blocks_per_group/8计算出块组中总共需要多少字节来表示所有块的状态
    for (int j = 0; j < sb->s_blocks_per_group / 8; j++) {
        // 检查当前字节是否有空闲块。0xFF意味着该字节表示的所有块都被占用
        if (block_bitmap[j] != 0xFF) {
            // 用于检查当前字节的每一位
            for (int k = 0; k < 8; k++) {
                // 用于检查当前字节的特定位是否为0，如果是，则找到了一个空闲块
                if (!(block_bitmap[j] & (1 << k))) {
                    // 打印出找到的空闲块的全局块号。计算方式是i*sb->s_blocks_per_group 计算出当前块组的第一个块号，j*8+k是在块组内的偏移
                    printf("            First Free Block in Group: %d\n", i * sb->s_blocks_per_group + j * 8 + k);
                    break;
                }
            }
            break;
        }
    }
}

void parse_inode_bitmap(FILE *fs, ext3_super_block *sb, ext3_group_desc *gdt, int i){
    // 解析inode位图
    
    unsigned char inode_bitmap[BLOCK_SIZE];
    fseek(fs, BLOCK_SIZE * gdt[i].bg_inode_bitmap, SEEK_SET);
    fread(inode_bitmap, sizeof(inode_bitmap), 1, fs);
    
    // 第一个循环：找出第一个空闲 inode 号
    int found = 0; // 用于标记是否找到了第一个空闲的 inode
    for (int j = 0; j < sb->s_inodes_per_group / 8 && !found; j++) {
        for (int k = 0; k < 8 && !found; k++) {
            int inode_num = i * sb->s_inodes_per_group + j * 8 + k + 1; // 计算 inode 号
            if (!(inode_bitmap[j] & (1 << k))) {
                printf("            First Free Inode in Group: %d\n", inode_num);
                found = 1;
            }
        }
    }
    
    // 第二个循环：分析非空闲 inode 的 i 节点表项
    int inode_table_block = gdt[i].bg_inode_table;
    for (int j = 0; j < sb->s_inodes_per_group / 8; j++) {
        for (int k = 0; k < 8; k++) {
            int inode_num = i * sb->s_inodes_per_group + j * 8 + k + 1;
            if (inode_bitmap[j] & (1 << k)) { // 已使用的 inode
                printf("            Used Inode Information in Group: %d\n", inode_num);
                
                // 读取 inode 表
                int inode_index = (inode_num - 1) % sb->s_inodes_per_group;
                int inode_offset = inode_index * sb->s_inode_size; // 使用超级块中指定的inode大小
                
                ext3_inode inode;
                fseek(fs, BLOCK_SIZE * inode_table_block + inode_offset, SEEK_SET);
                fread(&inode, sizeof(ext3_inode), 1, fs);
                
                // 打印 inode 信息
                //printf("Used Inode Information:\n");
                printf("                File Mode: %o\n", inode.i_mode);// 8进制输出
                printf("                Owner UID: %d\n", inode.i_uid);
                printf("                Size: %u bytes\n", inode.i_size);
            }
        }
    }
}

int main() {
    FILE *fs;
    ext3_super_block sb;            // 读取的超级块

    fs = fopen("experiment_fs.img", "r");
    if (fs == NULL) {
        perror("Error opening file");
        return EXIT_FAILURE;
    }
    
    //  解析超级块
    parse_superblock(fs, &sb);
    
    // 读取GDT
    ext3_group_desc *gdt = NULL;
    size_t block_group_count = read_gdt(fs, &sb, &gdt);
    
    printf("\nGroup Discriptors Information:\n");
    
    // 遍历每个组描述符
    for (int i = 0; i < block_group_count; i++) {
        printf("\n    Group %d:\n", i);
        printf("        Free Blocks Count: %u\n", gdt[i].bg_free_blocks_count);
        printf("        Free Inodes Count: %u\n", gdt[i].bg_free_inodes_count);
        printf("        Directories Count: %u\n", gdt[i].bg_used_dirs_count);
        printf("        Bitmaps Information:\n");
        
        // 解析块位图
        parse_block_bitmap(fs, &sb, gdt, i);
        
        // 解析inode 位图
        parse_inode_bitmap(fs, &sb, gdt, i);
        
    }

    free(gdt);
    fclose(fs);
    return EXIT_SUCCESS;
}
